package at.ac.tuwien.dse.health.admin

import javax.servlet.http.{HttpServletResponse, HttpServletRequest, HttpServlet}
import scala.collection.mutable
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
import javax.servlet.ServletConfig
import at.ac.tuwien.dse.health.admin.http.{RequestMapping, Redirect}
import scala.xml.Node
import scala.reflect.io.File
import scala.io.Source
import java.io.{PrintWriter, StringWriter}

/** Dispatches requests to methods using reflection. */
class DispatcherServlet extends HttpServlet {
	var requestMapping = mutable.LinkedHashMap[String, MethodSymbol]()

	/** Initializes this servlet my looking up request handler methods. */
	override def init(config: ServletConfig) {
		super.init(config)

		val annotationType = typeOf[RequestMapping]
		for (m <- typeOf[HealthAdminServlet].members) {
			val annotation = m.annotations find(_.tpe == annotationType)
			if (annotation.isDefined) {
				val url: String = "\"?/*([^\"]*)/*\"?".r replaceFirstIn(annotation.get scalaArgs 0 toString(), "$1")
				requestMapping += (url -> m.asMethod)
			}
		}
	}

	override def doGet(req: HttpServletRequest, resp: HttpServletResponse) {
		val result = try dispatch(req, resp)
		catch { case e: Throwable => e }

		val text = convert(result, req, resp)
		if (text != null) resp.getWriter write text.toString
	}

	/** Invokes the appropriate request handler method and returns its result. */
	def dispatch(req: HttpServletRequest, resp: HttpServletResponse): AnyRef = {
		val methodSymbol = requestMapping getOrElse(getRelativeURL(req), null)

		if (methodSymbol != null) {
			val method = currentMirror reflect this reflectMethod methodSymbol
			method(req, resp).asInstanceOf[AnyRef]
		} else {
			Redirect("error")
		}
	}

	/** Converts the given value to a form that can be sent as response. */
	def convert(value: AnyRef, req: HttpServletRequest, resp: HttpServletResponse): AnyRef = {
		value match {
			case s: String => s
			case r: Redirect => {resp.sendRedirect(r.url); null}
			case n: Node => applyTemplate(n) buildString false
			case f: File => convert(f.jfile, req, resp)
			case f: java.io.File => Source fromFile f
			case t: Throwable => {
				val str = new StringWriter()
				val out = new PrintWriter(str)
				t.printStackTrace(out)
				str.toString
			}
			case o: Any => o
		}
	}

	/** Returns the relative request URL stripping context and servlet path as well as leading and trailing slashes. */
	def getRelativeURL(req: HttpServletRequest) = {
		req.getRequestURI.substring((req.getContextPath + req.getServletPath).length) stripPrefix "/" stripSuffix "/"
	}

	/** Applies the template to the given [[scala.xml.Node]]. */
	private[admin] def applyTemplate(node: Node) = node

	@RequestMapping("error")
	private[admin] def error(req: HttpServletRequest, resp: HttpServletResponse) = {
<p>
	<h3 class="text-error">Error 404 - Not found</h3>
	<a href="javascript:history.back()">Back</a>
</p>
	}
}
